﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;

namespace Argos.Collections
{
	[Serializable]
	public class EventfulList<T> : IEventfulList<T>
	{

		public EventfulList() { }

		public EventfulList(IEnumerable<T> collection)
		{
			this.InnerList = new List<T>(collection);
		}

		public event EventHandler<EventArgs> Cleared;

		public event EventHandler<CancelEventArgs> Clearing;

		public event EventHandler<ItemEventArgs<T>> ItemAdded;

		public event EventHandler<CancelItemEventArgs<T>> ItemAdding;

		public event EventHandler<ItemEventArgs<T>> ItemSet;

		public event EventHandler<CancelItemEventArgs<T>> ItemSetting;

		public event EventHandler<ItemEventArgs<T>> ItemRemoved;

		public event EventHandler<CancelItemEventArgs<T>> ItemRemoving;



		public T this[int index]
		{
			get { return this.InnerList[index]; }
			set
			{
				CancelItemEventArgs<T> e = new CancelItemEventArgs<T>(value);
				this.OnItemSetting(e);
				if (!e.Cancel)
				{
					this.InnerList[index] = value;
					this.OnItemSet(new ItemEventArgs<T>(value));
				}
			}
		}

		public int Count
		{
			get { return this.InnerList.Count; }
		}

		public bool IsReadOnly
		{
			get { return ((ICollection<T>)this.InnerList).IsReadOnly; }
		}



		private List<T> InnerList = new List<T>();



		public void Add(T item)
		{
			CancelItemEventArgs<T> e = new CancelItemEventArgs<T>(item);
			this.OnItemAdding(e);
			if (!e.Cancel)
			{
				this.InnerList.Add(item);
				this.OnItemAdded(new ItemEventArgs<T>(item));
			}
		}

		public void AddRange(IEnumerable<T> items)
		{
			if(null != items)
			{
				foreach (T item in items)
				{
					this.Add(item);
				}
			}
		}

		public void Clear()
		{
			CancelEventArgs e = new CancelEventArgs();
			this.OnClearing(e);

			if (!e.Cancel)
			{
				this.InnerList.Clear();
				this.OnCleared(EventArgs.Empty);
			}
		}

		public bool Contains(T item)
		{
			return this.InnerList.Contains(item);
		}

		public void CopyTo(T[] array, int arrayIndex)
		{
			this.InnerList.CopyTo(array, arrayIndex);
		}

		public IEnumerator<T> GetEnumerator()
		{
			return this.InnerList.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return ((IEnumerable)this.InnerList).GetEnumerator();
		}

		public int IndexOf(T item)
		{
			return this.InnerList.IndexOf(item);
		}

		public void Insert(int index, T item)
		{
			CancelItemEventArgs<T> e = new CancelItemEventArgs<T>(item);
			this.OnItemAdding(e);
			if (!e.Cancel)
			{
				this.InnerList.Insert(index, item);
				this.OnItemAdded(new ItemEventArgs<T>(item));
			}
		}

		public void RemoveAt(int index)
		{

			if (this.Count > index)
			{
				CancelItemEventArgs<T> e = new CancelItemEventArgs<T>(this[index]);
				this.OnItemRemoving(e);
				if (e.Cancel)
				{
					return;
				}
			}

			T item = this[index];
			this.InnerList.RemoveAt(index);
			this.OnItemRemoved(new ItemEventArgs<T>(item));			
		}

		public bool Remove(T item)
		{
			bool result = false;

			CancelItemEventArgs<T> e = new CancelItemEventArgs<T>(item);
			this.OnItemRemoving(e);

			if (!e.Cancel)
			{
				result = this.InnerList.Remove(item);
				this.OnItemRemoved(new ItemEventArgs<T>(item));
			}

			return result;
		}

		public override string ToString()
		{
			return string.Format("Count = {0} ({1})", this.Count, typeof(T).Name);
		}



		protected virtual void OnCleared(EventArgs e)
		{
			if (null != this.Cleared)
			{
				this.Cleared.Invoke(this, e);
			}
		}

		protected virtual void OnClearing(CancelEventArgs e)
		{
			if (null != this.Clearing)
			{
				this.Clearing.Invoke(this, e);
			}
		}

		protected virtual void OnItemAdded(ItemEventArgs<T> e)
		{
			if (null != this.ItemAdded)
			{
				this.ItemAdded.Invoke(this, e);
			}
		}

		protected virtual void OnItemAdding(CancelItemEventArgs<T> e)
		{
			if (null != this.ItemAdding)
			{
				this.ItemAdding.Invoke(this, e);
			}
		}

		protected virtual void OnItemSet(ItemEventArgs<T> e)
		{
			if (null != this.ItemSet)
			{
				this.ItemSet.Invoke(this, e);
			}
		}

		protected virtual void OnItemSetting(CancelItemEventArgs<T> e)
		{
			if (null != this.ItemSetting)
			{
				this.ItemSetting.Invoke(this, e);
			}
		}

		protected virtual void OnItemRemoved(ItemEventArgs<T> e)
		{
			if (null != this.ItemRemoved)
			{
				this.ItemRemoved.Invoke(this, e);
			}
		}

		protected virtual void OnItemRemoving(CancelItemEventArgs<T> e)
		{
			if (null != this.ItemRemoving)
			{
				this.ItemRemoving.Invoke(this, e);
			}
		}

	}
}
